﻿using System;
using System.Diagnostics;

namespace MetalSoda
{
    /// <summary>
    /// One to zero or one association.
    /// </summary>
    /// <typeparam name="TRoot">Type of the association root</typeparam>
    /// <typeparam name="TRef">Type of the related object</typeparam>
    [Serializable]
    sealed class OneToOneAssociation<TRoot, TRef> 
        where TRoot : class 
        where TRef : class
    {
        #region Nested Types

        /// <summary>
        /// Association changed event data
        /// </summary>
        public sealed class AssociationChangedEventArgs : EventArgs
        {
            /// <summary>
            /// Association root
            /// </summary>
            public readonly TRoot Root;

            /// <summary>
            /// Related object
            /// </summary>
            public readonly TRef Ref;

            [DebuggerStepThrough]
            internal AssociationChangedEventArgs(TRoot root, TRef @ref)
            {
                Root = root;
                Ref = @ref;
            }
        }

        #endregion

        /// <summary>
        /// Association root
        /// </summary>
        public readonly TRoot Root;

        #region Private Fields
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private TRef m_ref;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly Action<AssociationChangedEventArgs> AssociationAdded;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly Action<AssociationChangedEventArgs> AssociationRemoved; 
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="OneToOneAssociation{TRoot, TRef}"/> class.
        /// </summary>
        /// <param name="root">Association root</param>
        /// <param name="assigned">Item added handler</param>
        /// <param name="unassigned">Item removed handler</param>
        [DebuggerStepThrough]
        public OneToOneAssociation(TRoot root,
            Action<AssociationChangedEventArgs> assigned,
            Action<AssociationChangedEventArgs> unassigned)
        {
            if (Object.ReferenceEquals(root, null)) { throw new ArgumentNullException("root"); }
            if (assigned == null) { throw new ArgumentNullException("assigned"); }
            if (unassigned == null) { throw new ArgumentNullException("unassigned"); }
            Root = root;
            AssociationAdded = assigned;
            AssociationRemoved = unassigned;
        }

        /// <summary>
        /// Related object
        /// </summary>
        public TRef Ref
        {
            [DebuggerStepThrough]
            get { return m_ref; }
            set 
            {
                if (Object.ReferenceEquals(m_ref, value)) { return; }
                var oldValue = m_ref;
                m_ref = value;
                if (oldValue != null) { AssociationRemoved(new AssociationChangedEventArgs(Root, oldValue)); }
                if (m_ref != null) { AssociationAdded(new AssociationChangedEventArgs(Root, m_ref)); }       
            }
        }
        
    }

    
}
